!|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

 module hmix_del4

!BOP
! !MODULE: hmix_del4

! !DESCRIPTION:
!  This module contains routines for computing biharmonic horizontal
!  diffusion of momentum and tracers.
!
! !REVISION HISTORY:
!  SVN:$Id: hmix_del4.F90 28439 2011-05-18 21:40:58Z njn01 $

! !USES:

   use POP_KindsMod
   use POP_ErrorMod
   use POP_FieldMod
   use POP_GridHorzMod
   use POP_HaloMod
   use POP_ReductionsMod

   use blocks
   use communicate
   use distribution
   use domain_size
   use domain
   use constants
   use grid
   use broadcast
   use io
   use diagnostics
   use exit_mod
   use tavg
   use time_management

   implicit none
   private
   save

! !PUBLIC MEMBER FUNCTIONS:

   public :: init_del4u,  &
             init_del4t,  &
             hdiffu_del4, &
             hdifft_del4

!EOP
!BOC
!-----------------------------------------------------------------------
!
!  private module variables
!
!  operator coefficients:
!
!  DT{N,S,E,W} = {N,S,E,W} coefficients of 5-point stencil for the
!                Del**2 operator before b.c.s have been applied
!  DU{C,N,S,E,W} = central and {N,S,E,W} coefficients of 5-point
!                  stencil for the Del**2 operator acting at U points
!                  (without metric terms that mix U,V)
!  DM{C,N,S,E,W} = central and {N,S,E,W} coefficients of 5-point
!                  stencil for the metric terms that mix U,V
!  DUM = central coefficient for metric terms that do not mix U,V
!
!-----------------------------------------------------------------------

   real (r8), dimension (:,:,:), allocatable :: &
      DTN,DTS,DTE,DTW,                          &
      DUC,DUN,DUS,DUE,DUW,                      &
      DMC,DMN,DMS,DME,DMW,                      &
      DUM,                                      &
      AHF,              &! variable mixing factor for tracer   mixing
      AMF                ! variable mixing factor for momentum mixing

   real (r8) ::         &
      ah,               &! horizontal tracer   mixing coefficient
      am                 ! horizontal momentum mixing coefficient

   logical (log_kind) :: &
      lvariable_hmixu,   &! spatially varying mixing coeffs
      lvariable_hmixt     ! spatially varying mixing coeffs

!EOC
!***********************************************************************

 contains

!***********************************************************************
!BOP
! !IROUTINE: init_del4u
! !INTERFACE:

 subroutine init_del4u(errorCode)

! !DESCRIPTION:
!  This routine calculates the coefficients of the 5-point stencils for
!  the biharmonic operator acting on momentum fields and also
!  calculates coefficients for all diffusive metric terms. See the
!  description under hdiffu for the form of the operator.
!
! !REVISION HISTORY:
!  same as module
!
! !OUTPUT PARAMETER:

   integer (POP_i4), intent(out) :: &
      errorCode             ! returned error code

!EOP
!BOC
!-----------------------------------------------------------------------
!
!  local variables
!
!-----------------------------------------------------------------------

   integer (int_kind) ::   &
      i,j,                 &! dummy loop indices
      iblock,              &! block index
      nml_error             ! error flag for namelist

   real (r8), dimension (:,:), allocatable :: &
      KXU,KYU,             &! metric factors
      DXKX,DYKY,DXKY,DYKX, &! d{x,y}k{x,y}
      WORK1,WORK2           ! temporary work space

   logical (log_kind) ::   &
      lauto_hmix,          &! flag to internally compute mixing coeff
      lvariable_hmix        ! flag to enable spatially varying mixing

   namelist /hmix_del4u_nml/ lauto_hmix, lvariable_hmix, am

   real (r8) ::            &
      amfmin, amfmax         ! min max mixing for varible mixing

!-----------------------------------------------------------------------
!
!  read input namelist to set options
!
!-----------------------------------------------------------------------

   errorCode = POP_Success

   lauto_hmix = .false.
   lvariable_hmix = .false.
   am = c0

   if (my_task == master_task) then
      open (nml_in, file=nml_filename, status='old',iostat=nml_error)
      if (nml_error /= 0) then
         nml_error = -1
      else
         nml_error =  1
      endif
      do while (nml_error > 0)
         read(nml_in, nml=hmix_del4u_nml,iostat=nml_error)
      end do
      if (nml_error == 0) close(nml_in)
   endif

   call broadcast_scalar(nml_error, master_task)
   if (nml_error /= 0) then
      call exit_POP(sigAbort,'ERROR reading hmix_del4u_nml')
   endif

   if (my_task == master_task) then

      write(stdout,blank_fmt)
      write(stdout,'(a34)') 'Biharmonic momentum mixing options'
      write(stdout,blank_fmt)

      !***
      !*** automatically set horizontal mixing coefficients
      !*** if requested
      !***

      if (lauto_hmix) then
         am = -0.6e20_r8*(1280.0_r8/float(nx_global))
         write(stdout,'(a44)') &
              'Horizontal viscosity computed automatically:'
      else
         write(stdout,'(a33)') 'Using input horizontal viscosity:'
      endif
      write(stdout,'(a7,2x,1pe12.5)') '  am =',am

      lvariable_hmixu = lvariable_hmix

      if (.not. lvariable_hmixu) then
         write(stdout,'(a44)') &
                  'Variable horizontal momentum mixing disabled'
      endif

   endif

   call broadcast_scalar(lvariable_hmixu, master_task)
   call broadcast_scalar(am,              master_task)

!-----------------------------------------------------------------------
!
!  set spatially variable mixing arrays if requested
!
!  this applies only to momentum or tracer mixing
!  with del2 or del4 options
!
!  functions describing spatial dependence of horizontal mixing
!  coefficients:   am -> am*AHM
!
!  for standard laplacian  mixing they scale like (cell area)**0.5
!  (note: these forms assume a global mesh with a grid line along
!  the equator, and the mixing coefficients are the set relative
!  to the average grid spacing at the equator - for cells of
!  this size AMF = AHF = 1.0).
!
!-----------------------------------------------------------------------

   if (lvariable_hmixu) then

      amfmin = c1
      amfmax = c1

      allocate(AMF(nx_block,ny_block,nblocks_clinic))

      do iblock=1,nblocks_clinic
         AMF(:,:,iblock) = (UAREA(:,:,iblock)/uarea_equator)**1.5
      end do

      amfmin = POP_GlobalMinval(AMF, POP_distrbClinic, errorCode, CALCU)
      amfmax = POP_GlobalMaxval(AMF, POP_distrbClinic, errorCode, CALCU)

      if (my_task == master_task) then
         write(stdout,'(a37)') 'Variable horizontal viscosity enabled'
         write(stdout,'(a12,1x,1pe12.5,3x,a9,1x,1pe12.5)') &
              '  Min AMF =',amfmin,'Max AMF =',amfmax
      endif

      call POP_HaloUpdate(AMF, POP_haloClinic, POP_gridHorzLocNECorner,&
                               POP_fieldKindScalar, errorCode,         &
                               fillValue = 0.0_POP_r8)

      if (errorCode /= POP_Success) then
         call POP_ErrorSet(errorCode, &
            'init_del4u: error updating halo for AMF')
         return
      endif

   else

      !*** allocate AMF temporarily to simplify setup

      allocate(AMF(nx_block,ny_block,nblocks_clinic))
      AMF = c1

   endif

!-----------------------------------------------------------------------
!
!  calculate operator weights
!
!-----------------------------------------------------------------------

   allocate(DUC(nx_block,ny_block,nblocks_clinic), &
            DUN(nx_block,ny_block,nblocks_clinic), &
            DUS(nx_block,ny_block,nblocks_clinic), &
            DUE(nx_block,ny_block,nblocks_clinic), &
            DUW(nx_block,ny_block,nblocks_clinic), &
            DMC(nx_block,ny_block,nblocks_clinic), &
            DMN(nx_block,ny_block,nblocks_clinic), &
            DMS(nx_block,ny_block,nblocks_clinic), &
            DME(nx_block,ny_block,nblocks_clinic), &
            DMW(nx_block,ny_block,nblocks_clinic), &
            DUM(nx_block,ny_block,nblocks_clinic))

   allocate(KXU   (nx_block,ny_block), &
            KYU   (nx_block,ny_block), &
            DXKX  (nx_block,ny_block), &
            DYKY  (nx_block,ny_block), &
            DXKY  (nx_block,ny_block), &
            DYKX  (nx_block,ny_block), &
            WORK1 (nx_block,ny_block), &
            WORK2 (nx_block,ny_block))

   do iblock=1,nblocks_clinic

!-----------------------------------------------------------------------
!
!     calculate central and {N,S,E,W} coefficients for
!     Del**2 (without metric terms) acting on momentum.
!
!-----------------------------------------------------------------------

      WORK1 = HUS(:,:,iblock)/HTE(:,:,iblock)

      DUS(:,:,iblock) = WORK1*UAREA_R(:,:,iblock)
      DUN(:,:,iblock) = eoshift(WORK1,dim=2,shift=1)*UAREA_R(:,:,iblock)
      !*** DUN invalid now in northernmost ghost cell

      WORK1 = HUW(:,:,iblock)/HTN(:,:,iblock)

      DUW(:,:,iblock) = WORK1*UAREA_R(:,:,iblock)
      DUE(:,:,iblock) = eoshift(WORK1,dim=1,shift=1)*UAREA_R(:,:,iblock)
      !*** DUE invalid now in easternmost ghost cell

!-----------------------------------------------------------------------
!
!     coefficients for metric terms in Del**2(U)
!     and for metric advection terms (KXU,KYU)
!
!-----------------------------------------------------------------------

      KXU = (eoshift(HUW(:,:,iblock),dim=1,shift=1) - &
             HUW(:,:,iblock))*UAREA_R(:,:,iblock)
      !*** KXU invalid in easternmost ghost cell
      KYU = (eoshift(HUS(:,:,iblock),dim=2,shift=1) - &
             HUS(:,:,iblock))*UAREA_R(:,:,iblock)
      !*** KYU invalid in northernmost ghost cell

      WORK1 = (HTE(:,:,iblock) -                         &
               eoshift(HTE(:,:,iblock),dim=1,shift=-1))* &
               TAREA_R(:,:,iblock)  ! KXT
      !*** WORK1 invalid in westernmost ghost cell
      WORK2 = eoshift(WORK1,dim=1,shift=1) - WORK1
      !*** WORK2 invalid in both easternmost and
      !***                       westernmost ghost cell

      DXKX = p5*(WORK2 + eoshift(WORK2,dim=2,shift=1))*DXUR(:,:,iblock)
      !*** DXKX invalid in east/west/northernmost ghost cells

      WORK2 = eoshift(WORK1,dim=2,shift=1) - WORK1
      !*** WORK2 invalid in west/northernmost ghost cells

      DYKX = p5*(WORK2 + eoshift(WORK2,dim=1,shift=1))*DYUR(:,:,iblock)
      !*** DYKX invalid in east/west/northernmost ghost cells

      WORK1 = (HTN(:,:,iblock) -                         &
               eoshift(HTN(:,:,iblock),dim=2,shift=-1))* &
              TAREA_R(:,:,iblock)  ! KYT
      !*** WORK1 invalid in southernmost ghost cell
      WORK2 = eoshift(WORK1,dim=2,shift=1) - WORK1
      !*** WORK2 invalid in north/southernmost ghost cells

      DYKY = p5*(WORK2 + eoshift(WORK2,dim=1,shift=1))*DYUR(:,:,iblock)
      !*** DYKY invalid in east/north/southernmost ghost cells

      WORK2 = eoshift(WORK1,dim=1,shift=1) - WORK1
      !*** WORK2 invalid in east/southernmost ghost cells

      DXKY = p5*(WORK2 + eoshift(WORK2,dim=2,shift=1))*DXUR(:,:,iblock)
      !*** DYKY invalid in east/north/southernmost ghost cells

      DUM(:,:,iblock) = -(DXKX + DYKY + c2*(KXU**2 + KYU**2))
      DMC(:,:,iblock) = DXKY - DYKX

!-----------------------------------------------------------------------
!
!     calculate central and {N,S,E,W} coefficients for
!     metric mixing terms which mix U,V.
!
!-----------------------------------------------------------------------

      DME(:,:,iblock) =  c2*KYU/(HTN(:,:,iblock) + &
                                 eoshift(HTN(:,:,iblock),dim=1,shift=1))
      DMN(:,:,iblock) = -c2*KXU/(HTE(:,:,iblock) + &
                                 eoshift(HTE(:,:,iblock),dim=2,shift=1))

   end do

   DUC = -(DUN + DUS + DUE + DUW)               ! scalar laplacian
   DMW = -DME
   DMS = -DMN

!-----------------------------------------------------------------------
!
!  free up memory
!
!-----------------------------------------------------------------------

   deallocate(KXU, KYU,               &
              DXKX, DYKY, DXKY, DYKX, &
              WORK1, WORK2)

   if (.not. lvariable_hmixu) deallocate(AMF)

!-----------------------------------------------------------------------
!EOC

 end subroutine init_del4u

!***********************************************************************
!BOP
! !IROUTINE: init_del4t
! !INTERFACE:

 subroutine init_del4t(errorCode)

! !DESCRIPTION:
!  This routine reads parameters for biharmonic tracer mixing and
!  calculates the coefficients of the 5-point stencils for
!  the biharmonic operator acting on tracer fields.  See the hdifft
!  routine for a description of the operator.
!
! !REVISION HISTORY:
!  same as module
!
! !OUTPUT PARAMETERS:

   integer (POP_i4), intent(out) :: &
      errorCode         ! returned error code

!EOP
!BOC
!-----------------------------------------------------------------------
!
!  local variables
!
!-----------------------------------------------------------------------

   integer (int_kind) :: &
      i,j,               &! dummy loop indices
      iblock,            &! block index
      nml_error           ! error flag for namelist

   real (r8), dimension (:,:), allocatable :: &
      WORK1                ! temporary work space

   logical (log_kind) :: &
      lauto_hmix,        &! true to automatically determine mixing coeff
      lvariable_hmix      ! true for spatially varying mixing

   namelist /hmix_del4t_nml/ lauto_hmix, lvariable_hmix, ah

   real (r8) ::          &
      ahfmin, ahfmax      ! min max mixing for varible mixing

!-----------------------------------------------------------------------
!
!  read input namelist to set options
!
!-----------------------------------------------------------------------

   errorCode = POP_Success

   lauto_hmix = .false.
   lvariable_hmix = .false.
   ah = c0

   if (my_task == master_task) then
      open (nml_in, file=nml_filename, status='old',iostat=nml_error)
      if (nml_error /= 0) then
         nml_error = -1
      else
         nml_error =  1
      endif
      do while (nml_error > 0)
         read(nml_in, nml=hmix_del4t_nml,iostat=nml_error)
      end do
      if (nml_error == 0) close(nml_in)
   endif

   call broadcast_scalar(nml_error, master_task)
   if (nml_error /= 0) then
      call exit_POP(sigAbort,'ERROR reading hmix_del4t_nml')
   endif

   if (my_task == master_task) then

      write(stdout,blank_fmt)
      write(stdout,'(a32)') 'Biharmonic tracer mixing options'
      write(stdout,blank_fmt)

      !***
      !***  automatically set horizontal mixing coefficients
      !***  if requested
      !***

      if (lauto_hmix) then
         ah = -0.2e20_r8*(1280.0_r8/float(nx_global))
         write(stdout,'(a46)') &
           'Horizontal diffusivity computed automatically:'
      else
         write(stdout,'(a35)') 'Using input horizontal diffusivity:'
      endif
      write(stdout,'(a7,2x,1pe12.5)') '  ah =',ah

      lvariable_hmixt = lvariable_hmix

      if (.not. lvariable_hmixt) then
         write(stdout,'(a43)') &
              'Variable horizontal tracer mixing disabled'
      endif

   endif

   call broadcast_scalar(lvariable_hmixt, master_task)
   call broadcast_scalar(ah,              master_task)

!-----------------------------------------------------------------------
!
!  set spatially variable mixing arrays if requested
!
!  this applies only to momentum or tracer mixing
!  with del2 or del4 options
!
!  functions describing spatial dependence of horizontal mixing
!  coefficients:   ah -> ah*AHF
!
!  for standard laplacian  mixing they scale like (cell area)**0.5
!  (note: these forms assume a global mesh with a grid line along
!  the equator, and the mixing coefficients are the set relative
!  to the average grid spacing at the equator - for cells of
!  this size AMF = AHF = 1.0).
!
!-----------------------------------------------------------------------

   if (lvariable_hmixt) then

      ahfmin = c1
      ahfmax = c1

      allocate(AHF(nx_block,ny_block,nblocks_clinic))

      do iblock=1,nblocks_clinic
         AHF(:,:,iblock) = (TAREA(:,:,iblock)/uarea_equator)**1.5
      end do

      ahfmin = POP_GlobalMinval(AHF, POP_distrbClinic, errorCode, CALCT)
      ahfmax = POP_GlobalMaxval(AHF, POP_distrbClinic, errorCode, CALCT)

      if (my_task == master_task) then
         write(stdout,'(a39)') &
               'Variable horizontal diffusivity enabled'
         write(stdout,'(a12,1x,1pe12.5,3x,a9,1x,1pe12.5)') &
               '  Min AHF =',ahfmin,'Max AHF =',ahfmax
      endif

      call POP_HaloUpdate(AHF, POP_haloClinic, POP_gridHorzLocCenter, &
                               POP_fieldKindScalar, errorCode,        &
                               fillValue = 0.0_POP_r8)

      if (errorCode /= POP_Success) then
         call POP_ErrorSet(errorCode, &
            'init_del4t: error updating halo for AHF')
         return
      endif

   endif

!-----------------------------------------------------------------------
!
!  calculate {N,S,E,W} coefficients for Del**2 acting on tracer
!  fields (for tracers, the central coefficient is calculated as
!  minus the sum of these after boundary conditions are applied).
!
!-----------------------------------------------------------------------

   allocate(DTN(nx_block,ny_block,nblocks_clinic),  &
            DTS(nx_block,ny_block,nblocks_clinic),  &
            DTE(nx_block,ny_block,nblocks_clinic),  &
            DTW(nx_block,ny_block,nblocks_clinic))

   allocate(WORK1 (nx_block,ny_block))

   do iblock=1,nblocks_clinic

      WORK1 = HTN(:,:,iblock)/HUW(:,:,iblock)

      DTN(:,:,iblock) = WORK1*TAREA_R(:,:,iblock)
      DTS(:,:,iblock) = eoshift(WORK1,dim=2,shift=-1)* &
                        TAREA_R(:,:,iblock)

      WORK1 = HTE(:,:,iblock)/HUS(:,:,iblock)

      DTE(:,:,iblock) = WORK1*TAREA_R(:,:,iblock)
      DTW(:,:,iblock) = eoshift(WORK1,dim=1,shift=-1)* &
                        TAREA_R(:,:,iblock)

   end do

!-----------------------------------------------------------------------
!
!  free up memory
!
!-----------------------------------------------------------------------

   deallocate(WORK1)

!-----------------------------------------------------------------------
!EOC

 end subroutine init_del4t

!***********************************************************************
!BOP
! !IROUTINE: hdiffu_del4
! !INTERFACE:

 subroutine hdiffu_del4(k,HDUK,HDVK,UMIXK,VMIXK,this_block)

! !DESCRIPTION:
!  This routine computes the horizontial diffusion of momentum
!  using a biharmonic ($\nabla^4$) operator, where the biharmonic
!  operator is implemented using a repeated application of the
!  Laplacian operator:
!  \begin{equation}
!  D_H(u) = \nabla^2(A_M\nabla^2(u))
!  \end{equation}
!  where
!  \begin{eqnarray}
!  \nabla^2(u) & = &  \Delta_x\delta_x[\Delta_y\delta_x(u)]/AREA
!                   + \Delta_y\delta_y[\Delta_x\delta_y(u)]/AREA \\
!              &   &  - u*[dxkx - dyky + 2(k_x^2 + k_y^2)]
!                + 2hy\delta_x(v) - 2k_x\delta_y(v) \nonumber \\
!  \nabla^2(v) & = & \Delta_x\delta_x[\Delta_y\delta_x(v)]/AREA
!                  + \Delta_y\delta_y[\Delta_x\delta_y(v)]/AREA \\
!              &   &  - v*[dxkx - dyky + 2(k_x^2 + k_y^2)]
!                - 2hy\delta_x(u) + 2k_x\delta_y(u) \nonumber
!  \end{eqnarray}
!
!  Boundary conditions are not explicitly imposed on
!  $\nabla^2(u,v)$ since $u = v = 0$ on the boundaries.
!  In this version of the model, the  boundary conditions
!  on $\nabla^2$ acting on $\nabla^2(u,v)$ are also that
!  $\nabla^2(u,v)$ vanishes at land points.
!
! !REVISION HISTORY:
!  same as module

! !INPUT PARAMETERS:

   integer (int_kind), intent(in) :: k  ! depth level index

   real (r8), dimension(nx_block,ny_block), intent(in) :: &
      UMIXK,             &! U velocity at level k and mix time level
      VMIXK               ! V velocity at level k and mix time level

   type (block), intent(in) :: &
      this_block             ! block info for this sub block

! !OUTPUT PARAMETERS:

   real (r8), dimension(nx_block,ny_block), intent(out) :: &
      HDUK,              &! Hdiff(Ub) at level k
      HDVK                ! Hdiff(Vb) at level k

!EOP
!BOC
!-----------------------------------------------------------------------
!
!     local variables
!
!-----------------------------------------------------------------------

   integer (int_kind) :: &
      i,j,               &! loop indices
      bid                 ! local address of current block

   real (r8), dimension (nx_block,ny_block) :: &
      D2UK,D2VK,        &! intermediate Del**2 results
      CC,               &! central 5-point weight
      CN,CS,CE,CW,      &! additional weights for partial bottom cells
      HDIFFCFL           ! local hdiff cfl number for diagnostics

!-----------------------------------------------------------------------
!
!  biharmonic mixing
!
!-----------------------------------------------------------------------

   bid = this_block%local_id

!-----------------------------------------------------------------------
!
!  calculate Del**2(U,V) without metric terms that mix U,V
!  add metric terms that mix U,V, and zero fields at land points
!
!-----------------------------------------------------------------------

   !*** add metric contribution to central coefficient
   CC = DUC(:,:,bid) + DUM(:,:,bid)
   D2UK = c0
   D2VK = c0

   if (partial_bottom_cells) then

      do j=this_block%jb-1,this_block%je+1
      do i=this_block%ib-1,this_block%ie+1
         CN(i,j) = DUN(i,j,bid)*min(DZU(i,j+1,k,bid),  &
                                    DZU(i,j  ,k,bid))/DZU(i,j  ,k,bid)
         CS(i,j) = DUS(i,j,bid)*min(DZU(i,j-1,k,bid),  &
                                    DZU(i,j  ,k,bid))/DZU(i,j  ,k,bid)
         CE(i,j) = DUE(i,j,bid)*min(DZU(i+1,j,k,bid),  &
                                    DZU(i  ,j,k,bid))/DZU(i  ,j,k,bid)
         CW(i,j) = DUW(i,j,bid)*min(DZU(i-1,j,k,bid),  &
                                    DZU(i  ,j,k,bid))/DZU(i  ,j,k,bid)
      end do
      end do

      do j=this_block%jb-1,this_block%je+1
      do i=this_block%ib-1,this_block%ie+1
         D2UK(i,j) = (CC (i,j    )*UMIXK(i  ,j  ) +        &
                      CN (i,j    )*UMIXK(i  ,j+1) +        &
                      CS (i,j    )*UMIXK(i  ,j-1) +        &
                      CE (i,j    )*UMIXK(i+1,j  ) +        &
                      CW (i,j    )*UMIXK(i-1,j  ))+        &
                     (DMC(i,j,bid)*VMIXK(i  ,j  ) +        &
                      DMN(i,j,bid)*VMIXK(i  ,j+1) +        &
                      DMS(i,j,bid)*VMIXK(i  ,j-1) +        &
                      DME(i,j,bid)*VMIXK(i+1,j  ) +        &
                      DMW(i,j,bid)*VMIXK(i-1,j  ))
      end do
      end do

      do j=this_block%jb-1,this_block%je+1
      do i=this_block%ib-1,this_block%ie+1
         D2VK(i,j) = (CC (i,j    )*VMIXK(i  ,j  ) +        &
                      CN (i,j    )*VMIXK(i  ,j+1) +        &
                      CS (i,j    )*VMIXK(i  ,j-1) +        &
                      CE (i,j    )*VMIXK(i+1,j  ) +        &
                      CW (i,j    )*VMIXK(i-1,j  ))-        &
                     (DMC(i,j,bid)*UMIXK(i  ,j  ) +        &
                      DMN(i,j,bid)*UMIXK(i  ,j+1) +        &
                      DMS(i,j,bid)*UMIXK(i  ,j-1) +        &
                      DME(i,j,bid)*UMIXK(i+1,j  ) +        &
                      DMW(i,j,bid)*UMIXK(i-1,j  ))
      end do
      end do

   else  ! no partial bottom cells

      do j=this_block%jb-1,this_block%je+1
      do i=this_block%ib-1,this_block%ie+1

         D2UK(i,j)  =(CC (i,j    )*UMIXK(i  ,j  ) +        &
                      DUN(i,j,bid)*UMIXK(i  ,j+1) +        &
                      DUS(i,j,bid)*UMIXK(i  ,j-1) +        &
                      DUE(i,j,bid)*UMIXK(i+1,j  ) +        &
                      DUW(i,j,bid)*UMIXK(i-1,j  ))+        &
                     (DMC(i,j,bid)*VMIXK(i  ,j  ) +        &
                      DMN(i,j,bid)*VMIXK(i  ,j+1) +        &
                      DMS(i,j,bid)*VMIXK(i  ,j-1) +        &
                      DME(i,j,bid)*VMIXK(i+1,j  ) +        &
                      DMW(i,j,bid)*VMIXK(i-1,j  ))
      end do
      end do

      do j=this_block%jb-1,this_block%je+1
      do i=this_block%ib-1,this_block%ie+1
         D2VK(i,j)  =(CC (i,j    )*VMIXK(i  ,j  ) +        &
                      DUN(i,j,bid)*VMIXK(i  ,j+1) +        &
                      DUS(i,j,bid)*VMIXK(i  ,j-1) +        &
                      DUE(i,j,bid)*VMIXK(i+1,j  ) +        &
                      DUW(i,j,bid)*VMIXK(i-1,j  ))-        &
                     (DMC(i,j,bid)*UMIXK(i  ,j  ) +        &
                      DMN(i,j,bid)*UMIXK(i  ,j+1) +        &
                      DMS(i,j,bid)*UMIXK(i  ,j-1) +        &
                      DME(i,j,bid)*UMIXK(i+1,j  ) +        &
                      DMW(i,j,bid)*UMIXK(i-1,j  ))
      end do
      end do

   endif ! partial bottom cells

   if (lvariable_hmixu) then
      where (k <= KMU(:,:,bid))
         D2UK = AMF(:,:,bid)*D2UK
         D2VK = AMF(:,:,bid)*D2VK
      elsewhere
         D2UK = c0
         D2VK = c0
      end where
   else
      where (k > KMU(:,:,bid))
         D2UK = c0
         D2VK = c0
      endwhere
   endif

!-----------------------------------------------------------------------
!
!  calculate Del**2(Del**2(U,V)) without metric terms that mix U,V
!  add metric terms, and zero fields at land points
!
!-----------------------------------------------------------------------

   HDUK = c0
   HDVK = c0

   if (partial_bottom_cells) then

      do j=this_block%jb,this_block%je
      do i=this_block%ib,this_block%ie
         HDUK(i,j)  = am*((CC (i,j    )*D2UK(i  ,j  ) +        &
                           CN (i,j    )*D2UK(i  ,j+1) +        &
                           CS (i,j    )*D2UK(i  ,j-1) +        &
                           CE (i,j    )*D2UK(i+1,j  ) +        &
                           CW (i,j    )*D2UK(i-1,j  ))+        &
                          (DMC(i,j,bid)*D2VK(i  ,j  ) +        &
                           DMN(i,j,bid)*D2VK(i  ,j+1) +        &
                           DMS(i,j,bid)*D2VK(i  ,j-1) +        &
                           DME(i,j,bid)*D2VK(i+1,j  ) +        &
                           DMW(i,j,bid)*D2VK(i-1,j  )))
      end do
      end do

      do j=this_block%jb,this_block%je
      do i=this_block%ib,this_block%ie
         HDVK(i,j)  = am*((CC (i,j    )*D2VK(i  ,j  ) +        &
                           CN (i,j    )*D2VK(i  ,j+1) +        &
                           CS (i,j    )*D2VK(i  ,j-1) +        &
                           CE (i,j    )*D2VK(i+1,j  ) +        &
                           CW (i,j    )*D2VK(i-1,j  ))-        &
                          (DMC(i,j,bid)*D2UK(i  ,j  ) +        &
                           DMN(i,j,bid)*D2UK(i  ,j+1) +        &
                           DMS(i,j,bid)*D2UK(i  ,j-1) +        &
                           DME(i,j,bid)*D2UK(i+1,j  ) +        &
                           DMW(i,j,bid)*D2UK(i-1,j  )))
      end do
      end do

   else ! no partial bottom cells

      do j=this_block%jb,this_block%je
      do i=this_block%ib,this_block%ie
         HDUK(i,j)  = am*((CC (i,j    )*D2UK(i  ,j  ) +        &
                           DUN(i,j,bid)*D2UK(i  ,j+1) +        &
                           DUS(i,j,bid)*D2UK(i  ,j-1) +        &
                           DUE(i,j,bid)*D2UK(i+1,j  ) +        &
                           DUW(i,j,bid)*D2UK(i-1,j  ))+        &
                          (DMC(i,j,bid)*D2VK(i  ,j  ) +        &
                           DMN(i,j,bid)*D2VK(i  ,j+1) +        &
                           DMS(i,j,bid)*D2VK(i  ,j-1) +        &
                           DME(i,j,bid)*D2VK(i+1,j  ) +        &
                           DMW(i,j,bid)*D2VK(i-1,j  )))
      end do
      end do

      do j=this_block%jb,this_block%je
      do i=this_block%ib,this_block%ie
         HDVK(i,j)  = am*((CC (i,j    )*D2VK(i  ,j  ) +        &
                           DUN(i,j,bid)*D2VK(i  ,j+1) +        &
                           DUS(i,j,bid)*D2VK(i  ,j-1) +        &
                           DUE(i,j,bid)*D2VK(i+1,j  ) +        &
                           DUW(i,j,bid)*D2VK(i-1,j  ))-        &
                          (DMC(i,j,bid)*D2UK(i  ,j  ) +        &
                           DMN(i,j,bid)*D2UK(i  ,j+1) +        &
                           DMS(i,j,bid)*D2UK(i  ,j-1) +        &
                           DME(i,j,bid)*D2UK(i+1,j  ) +        &
                           DMW(i,j,bid)*D2UK(i-1,j  )))
      end do
      end do

   endif ! partial bottom cells

   where (k > KMU(:,:,bid))
      HDUK = c0
      HDVK = c0
   endwhere

!-----------------------------------------------------------------------
!
!  compute cfl diagnostics if it is time
!
!-----------------------------------------------------------------------

   if (ldiag_cfl) then
      if (lvariable_hmixu) then
         HDIFFCFL = merge(-c16*am*AMF(:,:,bid)*                    &
                         (DXUR(:,:,bid)**2 + DYUR(:,:,bid)**2)**2, &
                          c0, KMU(:,:,bid) > k)
      else
         HDIFFCFL = merge(-c16*am*                                 &
                         (DXUR(:,:,bid)**2 + DYUR(:,:,bid)**2)**2, &
                          c0, KMU(:,:,bid) > k)
      endif
      HDIFFCFL = abs(HDIFFCFL)
      call cfl_hdiff(k,bid,HDIFFCFL,2,this_block)
   endif

!-----------------------------------------------------------------------
!EOC

 end subroutine hdiffu_del4

!***********************************************************************
!BOP
! !IROUTINE: hdifft_del4
! !INTERFACE:

 subroutine hdifft_del4(k,HDTK,TMIX,tavg_HDIFE_TRACER,tavg_HDIFN_TRACER,this_block)

! !DESCRIPTION:
!  This routine computes the horizontial diffusion of tracers
!  using a biharmonic ($\nabla^4$) operator, where the biharmonic
!  operator is implemented using a repeated application of the
!  Laplacian operator:
!  \begin{equation}
!  D_H(\phi) = \nabla^2(A_H\nabla^2(\phi))
!  \end{equation}
!  with
!  \begin{equation}
!  \nabla^2(\phi) = \Delta_x\delta_x[\Delta_y\delta_x(\phi)]/AREA
!                 + \Delta_y\delta_y[\Delta_x\delta_y(\phi)]/AREA
!  \end{equation}
!
!   The boundary conditions are the same on both
!   applications of the $\nabla^2$ operator:  the gradient of both
!   $T$ and $\nabla^2(T)$ vanishes across lateral boundaries.
!
! !REVISION HISTORY:
!  same as module

! !INPUT PARAMETERS:

   integer (int_kind), intent(in) :: k  ! depth level index

   real (r8), dimension(nx_block,ny_block,km,nt), intent(in) :: &
      TMIX                   ! tracers at mix time level

   integer (int_kind), dimension(nt), intent(in) :: &
      tavg_HDIFE_TRACER, &! tavg id for east face diffusive flux of tracer
      tavg_HDIFN_TRACER   ! tavg id for north face diffusive flux of tracer

   type (block), intent(in) :: &
      this_block             ! block info for this sub block

! !OUTPUT PARAMETERS:

   real (r8), dimension(nx_block,ny_block,nt) :: &
      HDTK                ! HDIFF(T) for tracer n at level k

!EOP
!BOC
!-----------------------------------------------------------------------
!
!     local variables
!
!-----------------------------------------------------------------------

   integer (int_kind) :: &
      i,j,n,             &! dummy loop indices
      bid                 ! local block address

   real (r8), dimension(nx_block,ny_block) :: &
      D2TK,              &! intermediate Del**2 result
      CC,CN,CS,CE,CW,    &! five point stencil coefficients
      WORK,              &! temp array for tavg quantities
      HDIFFCFL            ! local hdiff cfl number for diagnostics

!-----------------------------------------------------------------------
!
!  biharmonic mixing
!
!-----------------------------------------------------------------------

   bid = this_block%local_id

!-----------------------------------------------------------------------
!
!  implement boundary conditions by setting
!  stencil coefficients to zero at land points.
!
!-----------------------------------------------------------------------

   if (partial_bottom_cells) then
      CN = c0
      CS = c0
      CE = c0
      CW = c0

      do j=this_block%jb-1,this_block%je+1
      do i=this_block%ib-1,this_block%ie+1
         CN(i,j) = DTN(i,j,bid)*min(DZT(i,j  ,k,bid),  &
                                    DZT(i,j+1,k,bid))/DZT(i,j  ,k,bid)
         CS(i,j) = DTS(i,j,bid)*min(DZT(i,j  ,k,bid),  &
                                    DZT(i,j-1,k,bid))/DZT(i,j  ,k,bid)
         CE(i,j) = DTE(i,j,bid)*min(DZT(i  ,j,k,bid),  &
                                    DZT(i+1,j,k,bid))/DZT(i  ,j,k,bid)
         CW(i,j) = DTW(i,j,bid)*min(DZT(i  ,j,k,bid),  &
                                    DZT(i-1,j,k,bid))/DZT(i  ,j,k,bid)
      end do
      end do

      where (k > KMT(:,:,bid) .or. k > KMTN(:,:,bid)) CN = c0
      where (k > KMT(:,:,bid) .or. k > KMTS(:,:,bid)) CS = c0
      where (k > KMT(:,:,bid) .or. k > KMTE(:,:,bid)) CE = c0
      where (k > KMT(:,:,bid) .or. k > KMTW(:,:,bid)) CW = c0
   else
      CN = merge(DTN(:,:,bid), c0, (k <= KMTN(:,:,bid)) .and.  &
                                   (k <= KMT (:,:,bid)))
      CS = merge(DTS(:,:,bid), c0, (k <= KMTS(:,:,bid)) .and.  &
                                   (k <= KMT (:,:,bid)))
      CE = merge(DTE(:,:,bid), c0, (k <= KMTE(:,:,bid)) .and.  &
                                   (k <= KMT (:,:,bid)))
      CW = merge(DTW(:,:,bid), c0, (k <= KMTW(:,:,bid)) .and.  &
                                   (k <= KMT (:,:,bid)))
   endif

   CC = -(CN + CS + CE + CW)  ! central coefficient

   WORK = c0

   do n = 1,nt
!-----------------------------------------------------------------------
!
!     calculate Del**2(T)
!
!-----------------------------------------------------------------------

      if (lvariable_hmixt) then

         do j=this_block%jb-1,this_block%je+1
         do i=this_block%ib-1,this_block%ie+1

            D2TK(i,j) = AHF(i,j,bid)*                    &
                       (CC(i,j)*TMIX(i  ,j  ,k,n) +      &
                        CN(i,j)*TMIX(i  ,j+1,k,n) +      &
                        CS(i,j)*TMIX(i  ,j-1,k,n) +      &
                        CE(i,j)*TMIX(i+1,j  ,k,n) +      &
                        CW(i,j)*TMIX(i-1,j  ,k,n))

         end do
         end do

      else

         do j=this_block%jb-1,this_block%je+1
         do i=this_block%ib-1,this_block%ie+1

            D2TK(i,j) = CC(i,j)*TMIX(i  ,j  ,k,n) +      &
                        CN(i,j)*TMIX(i  ,j+1,k,n) +      &
                        CS(i,j)*TMIX(i  ,j-1,k,n) +      &
                        CE(i,j)*TMIX(i+1,j  ,k,n) +      &
                        CW(i,j)*TMIX(i-1,j  ,k,n)

         end do
         end do

      endif

!-----------------------------------------------------------------------
!
!     calculate Del**2(Del**2(T))
!     multiply by diffusivity
!
!-----------------------------------------------------------------------

      HDTK(:,:,n) = c0

      do j=this_block%jb,this_block%je
      do i=this_block%ib,this_block%ie

         HDTK(i,j,n) = ah*(CC(i,j)*D2TK(i  ,j  ) +     &
                           CN(i,j)*D2TK(i  ,j+1) +     &
                           CS(i,j)*D2TK(i  ,j-1) +     &
                           CE(i,j)*D2TK(i+1,j  ) +     &
                           CW(i,j)*D2TK(i-1,j  ))

      end do
      end do

      if (mix_pass /= 1) then
         if (accumulate_tavg_now(tavg_HDIFE_TRACER(n))) then
            do j=this_block%jb,this_block%je
            do i=this_block%ib,this_block%ie
               WORK(i,j) = ah*CE(i,j)*(D2TK(i+1,j)-D2TK(i,j))
            enddo
            enddo
            call accumulate_tavg_field(WORK,tavg_HDIFE_TRACER(n),bid,k)
         endif

         if (accumulate_tavg_now(tavg_HDIFN_TRACER(n))) then
            do j=this_block%jb,this_block%je
            do i=this_block%ib,this_block%ie
               WORK(i,j) = ah*CN(i,j)*(D2TK(i,j+1)-D2TK(i,j))
            enddo
            enddo
            call accumulate_tavg_field(WORK,tavg_HDIFN_TRACER(n),bid,k)
         endif
      endif

   enddo

!-----------------------------------------------------------------------
!
!  compute cfl diagnostics if it is time
!
!-----------------------------------------------------------------------

   if (ldiag_cfl) then
      if (lvariable_hmixt) then
         HDIFFCFL = merge(-c16*ah*AHF(:,:,bid)*                    &
                         (DXTR(:,:,bid)**2 + DYTR(:,:,bid)**2)**2, &
                          c0, KMT(:,:,bid) > k)
      else
         HDIFFCFL = merge(-c16*ah*                                 &
                         (DXTR(:,:,bid)**2 + DYTR(:,:,bid)**2)**2, &
                          c0, KMT(:,:,bid) > k)
      endif
      HDIFFCFL = abs(HDIFFCFL)
      call cfl_hdiff(k,bid,HDIFFCFL,1,this_block)
   endif

!-----------------------------------------------------------------------
!EOC

 end subroutine hdifft_del4

!***********************************************************************

 end module hmix_del4

!|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
